﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Log;

namespace UtilZ.Tool.WatchdogPlugin
{
    public partial class AppItem
    {
        /// <summary>
        /// 线程锁
        /// </summary>
        private readonly object _lock = new object();

        /// <summary>
        /// 应用进程
        /// </summary>
        private Process _process = null;

        /// <summary>
        /// 第一个应用启动时间戳
        /// </summary>
        private long _firstAppStartTimestamp = 0;

        /// <summary>
        /// 心跳检测时间戳
        /// </summary>
        private long _heartTimestamp = 0;



        /// <summary>
        /// 当前应用是否延迟启动
        /// </summary>
        /// <returns></returns>
        internal bool IsDelayStart()
        {
            return this._delayStartMilliseconds > 0;
        }

        private bool AllowStart()
        {
            return TimeEx.GetTimestamp() - this._firstAppStartTimestamp >= this._delayStartMilliseconds;
        }

        private bool IsTargetAppProcess(Process process)
        {
            try
            {
                if (process == null || process.MainModule == null)
                {
                    return false;
                }

                if (!string.Equals(process.MainModule.FileName, this._exeFilePath, StringComparison.OrdinalIgnoreCase))
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
            }

            if (!string.IsNullOrEmpty(this._arguments))
            {
                try
                {
                    if (process.StartInfo != null)
                    {
                        return string.Equals(process.StartInfo.Arguments, this._arguments, StringComparison.OrdinalIgnoreCase);
                    }
                }
                catch (InvalidOperationException)
                {

                }

                //查询参数
                string processArgs = HostQueryHelper.QueryProArgs(process.Id);
                return string.Equals(processArgs, this._arguments, StringComparison.OrdinalIgnoreCase);
            }

            return true;

        }


        //private static string SubArgs(string commandLine)
        //{
        //    if (string.IsNullOrEmpty(commandLine))
        //    {
        //        return null;
        //    }

        //    int subIndex = -1;
        //    bool isFind = false;
        //    if (commandLine.StartsWith('"'))
        //    {
        //        for (int i = 1; i < commandLine.Length; i++)
        //        {
        //            if (isFind)
        //            {
        //                if (commandLine[i] != ' ')
        //                {
        //                    subIndex = i;
        //                    break;
        //                }
        //            }
        //            else
        //            {
        //                if (commandLine[i] == '"')
        //                {
        //                    isFind = true;
        //                }
        //            }
        //        }
        //    }
        //    else
        //    {
        //        for (int i = 0; i < commandLine.Length; i++)
        //        {
        //            if (isFind)
        //            {
        //                if (commandLine[i] != ' ')
        //                {
        //                    subIndex = i;
        //                    break;
        //                }
        //            }
        //            else
        //            {
        //                if (commandLine[i] == ' ')
        //                {
        //                    isFind = true;
        //                }
        //            }
        //        }
        //    }

        //    if (subIndex > 0)
        //    {
        //        return commandLine.Substring(subIndex).Trim(_argsTrimChas);
        //    }
        //    else
        //    {
        //        return commandLine.Trim(_argsTrimChas);
        //    }
        //}
        //private readonly static char[] _argsTrimChas = new char[] { '"', '\'', ' ' };

        //private static string GetCommandLineArgs(int pid)
        //{
        //    try
        //    {
        //        using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + pid))
        //        {
        //            using (var objects = searcher.Get())
        //            {
        //                var @object = objects.Cast<ManagementBaseObject>().SingleOrDefault();
        //                return @object?["CommandLine"]?.ToString() ?? "";
        //            }
        //        }
        //    }
        //    catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005)
        //    {
        //        // 没有对该进程的安全访问权限。
        //        return string.Empty;
        //    }
        //    catch (InvalidOperationException)
        //    {
        //        // 进程已退出。
        //        return string.Empty;
        //    }
        //}


        private Stopwatch _noRespondingWatch = null;
        private void FreeNoRespondingWatch()
        {
            var noRespondingWatch = this._noRespondingWatch;
            if (noRespondingWatch != null)
            {
                noRespondingWatch.Stop();
                this._noRespondingWatch = null;
            }
        }

        private void UpdateVersion()
        {
            try
            {
                if (string.IsNullOrEmpty(this._exeFilePath) ||
                    !File.Exists(this._exeFilePath))
                {
                    Loger.Warn($"获取文件\"{this._exeFilePath}\"版本号失败,文件为空或文件不存在,版本号为空");
                    this._softwVer = string.Empty;
                    return;
                }

                //版本号
                var fvi = FileVersionInfo.GetVersionInfo(this._exeFilePath);
                //return fileVerInfo.ProductVersion;
                this._softwVer = $"{fvi.FileMajorPart}.{fvi.FileMinorPart}.{fvi.FileBuildPart}.{fvi.FilePrivatePart}";
            }
            catch (Exception ex)
            {
                Loger.Warn(ex, $"获取文件\"{this._exeFilePath}\"版本号异常,版本号为空");
                this._softwVer = null;
            }
        }

        private bool ProcessIsEnd()
        {
            var process = this._process;
            if (process != null && !process.HasExited)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private void UpdateRunStatus()
        {
            this._runStatus = !this.ProcessIsEnd();
            if (!this._runStatus)
            {
                this._startTime = null;
            }

            WatchdogLRPCHelper.PushAppChangedNotify(new AppChangedMessage(AppChangedMessage.RUN_STATUS_CHANGED, this._appName)
            {
                RunStatus = this._runStatus,
                IsMonitor = this._isMonitor,
                StartTime = this._startTime
            }); ;
        }





        private void Process_Exited(object sender, EventArgs e)
        {
            try
            {
                lock (this._lock)
                {
                    Loger.Info($"程序[{this._exeFilePath}]停止,正在启动...");
                    this.RemoveMonitor();
                    this.KillAppAllProcess();
                    this.UpdateRunStatus();

                    if (this._isMonitor)
                    {
                        this.PrimitiveStart();
                    }
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex, $"启动程序[{this._exeFilePath}]异常");
            }
        }


        private void AddMonitor()
        {
            if (this._process != null)
            {
                this._process.EnableRaisingEvents = true;
                this._process.Exited += Process_Exited;
            }
        }

        private void RemoveMonitor()
        {
            if (this._process != null)
            {
                this._process.EnableRaisingEvents = false;
                this._process.Exited -= Process_Exited;
            }
        }



        private Process[] GetCurrentProcessNameProcesss()
        {
            return Process.GetProcessesByName(this._processName).Where(t =>
            {
                try
                {
                    return t.MainModule != null && string.Equals(t.MainModule.FileName, this._exeFilePath, StringComparison.OrdinalIgnoreCase);
                }
                catch
                {
                    return false;
                }
            }).ToArray();
        }

        private void KillAppAllProcess(Process[] procesArr = null)
        {
            this.KillCurrentProcess();

            //当前进程杀死,那么同名进程一个都不放过
            if (procesArr == null)
            {
                procesArr = this.GetCurrentProcessNameProcesss();
            }

            foreach (var process in procesArr)
            {
                try
                {
                    if (this.IsTargetAppProcess(process))
                    {
                        process.Kill();                        
                    }
                }
                catch (InvalidOperationException)
                {

                }

                process.Dispose();
            }
        }

        private void KillCurrentProcess()
        {
            if (this._process != null)
            {
                this.RemoveMonitor();
                try
                {
                    this._process.Kill();
                    this._process.Dispose();
                }
                catch (InvalidOperationException)
                {

                }
                this._process = null;
            }
        }


        private void UpdateProcess(Process process)
        {
            this._process = process;
            this._processId = process.Id;
            this._startTime = process.StartTime;
            this._firstStart = false;
            this._heartTimestamp = TimeEx.GetTimestamp();
            this.UpdateRunStatus();
            this.AddMonitor();
        }


        private void PrimitiveStart()
        {
            this.FreeNoRespondingWatch();
            var startInfo = new ProcessStartInfo();
            startInfo.UseShellExecute = true;
            startInfo.FileName = this._exeFilePath;
            startInfo.Arguments = this._arguments;
            startInfo.WorkingDirectory = Path.GetDirectoryName(this._exeFilePath);
            startInfo.Verb = "runas";
            var process = Process.Start(startInfo);
            this.UpdateProcess(process);
            this.ResetProcessResourseUsageStatisticsVar();
        }





        internal bool AppIsRuning()
        {
            Process[] procesArr = this.GetCurrentProcessNameProcesss();
            bool isRuning = false;
            foreach (Process process in procesArr)
            {
                if (this.IsTargetAppProcess(process))
                {
                    isRuning = true;
                }

                process.Dispose();
            }

            return isRuning;
        }

        internal void StartCheck(long firstAppStartTimestamp)
        {
            lock (this._lock)
            {
                this.PrimitiveStartCheck(firstAppStartTimestamp);
            }
        }

        private void PrimitiveStartCheck(long firstAppStartTimestamp)
        {
            lock (this._lock)
            {
                this._firstAppStartTimestamp = firstAppStartTimestamp;

                if (this.IsDelayStart() && !this.AllowStart())
                {
                    //当需要延迟启动，但是进程已经启动，则先杀死进程
                    this.KillAppAllProcess();
                    this.EnableMonitor();//同时启用监视,并发送通知
                    return;
                }

                this.PrimitiveCheck();
            }
        }

        private void PrimitiveCheck()
        {
            var procesArr = this.GetCurrentProcessNameProcesss();
            int index = -1;
            for (int i = 0; i < procesArr.Length; i++)
            {
                if (this.IsTargetAppProcess(procesArr[i]))
                {
                    if (index >= 0)
                    {
                        //还有第二个或更多的同一个路径下进程且同参数,则杀死
                        procesArr[index].Kill();
                        procesArr[i].Kill();
                        procesArr[i].Dispose();

                        for (int j = i + 1; i < procesArr.Length; j++)
                        {
                            if (this.IsTargetAppProcess(procesArr[j]))
                            {
                                procesArr[j].Kill();
                                procesArr[j].Dispose();
                            }
                        }
                        index = -1;
                        break;
                    }
                    else
                    {
                        index = i;//找到第一个进程索引先记录下来
                    }
                }
                else
                {
                    procesArr[i].Dispose();
                }
            }

            if (index < 0)
            {
                //进程未启动，则启动
                this.PrimitiveStart();
            }
            else
            {
                if (this._process == null)
                {
                    //只有一个应用进程,直接更新状态
                    this.UpdateProcess(procesArr[index]);
                }
                else
                {
                    if (this._process.Id != procesArr[index].Id)
                    {
                        //进程换成别的了，先杀死旧的再启动
                        this.KillCurrentProcess();
                        procesArr[index].Kill();
                        procesArr[index].Dispose();
                        this.PrimitiveStart();
                    }
                    else
                    {
                        //是同一个应用进程,检查是否未响应,如果处于未响应状态,就杀死当前进程后重启
                        if (HostQueryHelper.IsNoResponse(this._process.MainWindowHandle))
                        {
                            //进程未响应,先计时，等超时后,再杀死旧的,重启一个
                            if (this._noRespondingWatch == null)
                            {
                                this._noRespondingWatch = Stopwatch.StartNew();
                            }

                            if (this._noRespondingWatch.Elapsed.TotalMilliseconds >= WatchdogConfig.Instance.NoRespondingMillisecondsTimeout)
                            {
                                //进程换成别的了，先杀死旧的再启动
                                this.KillCurrentProcess();
                                this.PrimitiveStart();
                            }
                        }
                        else
                        {
                            if (this._enableHeartCheck && TimeEx.GetTimestamp() - this._heartTimestamp >= this._heartTimeoutMilliseconds)
                            {
                                //心跳检测超时,先杀死旧的再启动
                                this.KillCurrentProcess();
                                this.PrimitiveStart();
                            }
                            else
                            {
                                //释放未响应检测
                                this.FreeNoRespondingWatch();
                            }
                        }
                    }
                }
            }
        }

        internal void NoMonitorUpdateProcess()
        {
            lock (this._lock)
            {
                var procesArr = this.GetCurrentProcessNameProcesss();
                int index = -1;
                for (int i = 0; i < procesArr.Length; i++)
                {
                    if (this.IsTargetAppProcess(procesArr[i]))
                    {
                        if (index >= 0)
                        {
                            //还有第二个或更多的同一个路径下进程且同参数,则杀死
                            procesArr[index].Kill();
                            procesArr[i].Kill();
                            procesArr[i].Dispose();


                            for (int j = i + 1; i < procesArr.Length; j++)
                            {
                                if (this.IsTargetAppProcess(procesArr[j]))
                                {
                                    procesArr[j].Kill();
                                    procesArr[j].Dispose();
                                }
                            }
                            index = -1;
                            break;
                        }
                        else
                        {
                            index = i;//找到第一个进程索引先记录下来
                        }
                    }
                    else
                    {
                        procesArr[i].Dispose();
                    }
                }

                if (index < 0)
                {
                    //进程未启动，不做处理
                    //this.PrimitiveStart();
                }
                else
                {
                    if (this._process == null)
                    {
                        //只有一个应用进程,直接更新状态
                        this.UpdateProcess(procesArr[index]);
                    }
                    else
                    {
                        if (this._process.Id != procesArr[index].Id)
                        {
                            //进程换成别的了，杀死旧的
                            this.KillCurrentProcess();
                            procesArr[index].Kill();
                            //this.PrimitiveStart();
                        }
                        else
                        {
                            //不是同一个应用进程不做操作
                        }
                    }
                }
            }
        }



        internal void Stop()
        {
            lock (this._lock)
            {
                try
                {
                    this._isMonitor = false;

                    Loger.Info($"程序[{this._exeFilePath}]正在停止...");
                    if (this._process != null)
                    {
                        this.KillAppAllProcess();
                        this.FreeNoRespondingWatch();
                        this.UpdateRunStatus();
                    }
                }
                catch (Exception ex)
                {
                    Loger.Error(ex, $"停止应用[{this._exeFilePath}]发生异常");
                }
                finally
                {
                    this.UpdateRunStatus();
                    Loger.Info($"程序[{this._exeFilePath}]停止完成");
                }
            }
        }

        internal void Start(long firstAppStartTimestamp)
        {
            lock (this._lock)
            {
                this._isMonitor = true;
                this.PrimitiveStartCheck(firstAppStartTimestamp);
            }
        }

        internal void Restart(long firstAppStartTimestamp)
        {
            lock (this._lock)
            {
                try
                {
                    this._isMonitor = true;
                    this.PrimitiveStartCheck(firstAppStartTimestamp);
                    Loger.Info($"重启程序[{this._exeFilePath}]成功");
                }
                catch (Exception ex)
                {
                    this.UpdateRunStatus();
                    Loger.Error(ex, $"停止应用[{this._exeFilePath}]发生异常");
                }
            }
        }

        internal void DisableMonitor()
        {
            lock (this._lock)
            {
                this._isMonitor = false;
                //this.RemoveMonitor();

                WatchdogLRPCHelper.PushAppChangedNotify(new AppChangedMessage(AppChangedMessage.MONITOR_STATUS_CHANGED, this._appName)
                {
                    IsMonitor = false
                });
            }
        }

        internal void EnableMonitor()
        {
            lock (this._lock)
            {
                try
                {
                    this._isMonitor = true;
                    WatchdogLRPCHelper.PushAppChangedNotify(new AppChangedMessage(AppChangedMessage.MONITOR_STATUS_CHANGED, this._appName)
                    {
                        IsMonitor = true
                    });

                    //以下代码没必要此处执行,如果进程没启动,则通过检查线程去启动进程
                    //if (this.ProcessIsEnd())
                    //{
                    //    this.KillProcess();
                    //    this.PrimitiveStart();
                    //}

                    //this.AddMonitor();
                }
                catch (Exception ex)
                {
                    Loger.Error(ex);
                    this.RemoveMonitor();
                }
            }
        }



        private bool _firstStart = true;
        /// <summary>
        /// 检查应用程序状态,当需要延时启动的程序需要重启时,返回true否则返回false
        /// </summary>
        /// <param name="crushProcessIdList"></param>
        /// <param name="crushProcessIdList"></param>
        /// <returns>当需要延时启动的程序需要重启时,返回true否则返回false</returns>
        internal bool Check(List<int> crushProcessIdList)
        {
            if (!this._isMonitor)
            {
                return false;
            }

            lock (this._lock)
            {
                if (crushProcessIdList != null && crushProcessIdList.Contains(this._processId))
                {
                    this.KillAppAllProcess();

                    if (this.IsDelayStart())
                    {
                        return true;
                    }
                    else
                    {
                        this.PrimitiveStart();
                        return false;
                    }
                }

                if (this.ProcessIsEnd())
                {
                    this.KillAppAllProcess();

                    if (this.IsDelayStart() && !this.AllowStart())
                    {
                        if (this._firstStart)
                        {
                            //延时启动,还不到时间，下回再说
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }

                    //允许启动，则启动
                    this.PrimitiveStart();
                }
                else
                {
                    this.PrimitiveCheck();
                }
            }

            return false;
        }



        internal void Reset()
        {
            this._firstStart = true;
            this._startTime = null;
            this._runStatus = false;
        }

        internal void Init()
        {
            if (string.IsNullOrWhiteSpace(this._appExeFilePath))
            {
                this._processName = null;
                return;
            }

            this._processName = Path.GetFileNameWithoutExtension(this._appExeFilePath);
            if (string.IsNullOrWhiteSpace(Path.GetPathRoot(this._appExeFilePath)))
            {
                string appExeFilePath = EnvironmentEx.GetFilePathFromEnvPath(this._appExeFilePath);
                if (File.Exists(appExeFilePath))
                {
                    this._exeFilePath = appExeFilePath;
                }
                else
                {
                    throw new ArgumentException($"应用\"{this._appName}\"对应的启动文件\"{this._appExeFilePath}\"不存在");
                }
            }
            else
            {
                this._exeFilePath = this._appExeFilePath;
            }

            if (File.Exists(this._exeFilePath))
            {
                this.UpdateVersion();
            }
        }


        private long _lastTotalProcessorTimestamp = 0;
        private TimeSpan _lastTotalProcessorTime = TimeSpan.Zero;

        private void ResetProcessResourseUsageStatisticsVar()
        {
            try
            {
                this._lastTotalProcessorTimestamp = TimeEx.GetTimestamp(this._process.StartTime);
            }
            catch (Exception ex)
            {
                Loger.Warn(ex);
                this._lastTotalProcessorTimestamp = -1;
            }

            this._lastTotalProcessorTime = TimeSpan.Zero;
        }

        internal void UpdateProcessResourseUsageRate()
        {
            lock (this._lock)
            {
                if (!this.ProcessIsEnd())
                {
                    try
                    {
                        Process process = this._process;
                        if (process == null)
                        {
                            this._lastTotalProcessorTime = TimeSpan.Zero;
                            this._CPURate = 0;
                            return;
                        }

                        if (this._lastTotalProcessorTime == TimeSpan.Zero)
                        {
                            this._lastTotalProcessorTime = process.TotalProcessorTime;
                            this._CPURate = 0;
                            return;
                        }

                        var lastTotalProcessorTime = this._lastTotalProcessorTime;
                        this._lastTotalProcessorTime = process.TotalProcessorTime;

                        var lastTotalProcessorTimestamp = this._lastTotalProcessorTimestamp;
                        this._lastTotalProcessorTimestamp = TimeEx.GetTimestamp();

                        TimeSpan cpuUsedMs = this._lastTotalProcessorTime - lastTotalProcessorTime;
                        long totalMsPassed = this._lastTotalProcessorTimestamp - lastTotalProcessorTimestamp;
                        var cpuUsageTotal = cpuUsedMs.TotalMilliseconds / (Environment.ProcessorCount * totalMsPassed);
                        int cpuRate = Convert.ToInt32(cpuUsageTotal * 100);
                        this._CPURate = cpuRate;
                        this._memUse = process.WorkingSet64;
                        this._thrNum = process.Threads.Count;
                        return;
                    }
                    catch (Exception ex)
                    {
                        Loger.Warn(ex);
                    }
                }

                this._CPURate = 0;
                this._memUse = 0L;
                this._thrNum = 0;
            }
        }


        internal void Update(AppItem appItem)
        {
            this._isMonitor = false;//先设定当前进程为不监视
            this._appName = appItem._appName;
            this._appExeFilePath = appItem._appExeFilePath;
            this._arguments = appItem._arguments;

            this._des = appItem._des;
            this._delayStartTime = appItem._delayStartTime;
            this._delayStartMilliseconds = appItem._delayStartMilliseconds;
            this._enableHeartCheck = appItem._enableHeartCheck;
            this._heartTimeoutSeconds = appItem._heartTimeoutSeconds;
            this._heartTimeoutMilliseconds = appItem._heartTimeoutMilliseconds;
            this.Init();

            this._isMonitor = appItem._isMonitor;//注:此行代码需要放到最后,否则负责进程检测的线程会先将程序启动起来
        }

        internal void UpdateHeartTimestamp()
        {
            this._heartTimestamp = TimeEx.GetTimestamp();
        }
    }
}
